using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace DrivingGame
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        GraphicsDevice device;
        AudioEngine audioEngine;
        WaveBank waveBank;
        SoundBank soundBank;
        Cue cueBackground;
        Cue cueRunning;
        Cue cueSliding;
        Cue cueBrake;


        Camera camera;

        // World environment
        World skybox;
        World ground;
        City city;

        // Game objects
        Player player;
        Computer computer;

        Car winner;
        Effect effect;
        BasicEffect basicEffect;
        Texture2D cityTexture;
        Texture2D finalSquareTexture;
        SpriteFont font;
        Vector2 fontPos;
        
        // Setting Class
        Setting setting;

        // Menu
        public GameState GameState
        {
            get { return currentGameState; }
            set { currentGameState = value; }
        }
        GameState currentGameState;

        bool gameOver = false;

        //Splash Screen
        SplashScreen splashScreen;
        
        //Dashboard
        Dashboard dashboard;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            setting = new Setting();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            splashScreen = new SplashScreen(this, Content);
            dashboard = new Dashboard(this, Content);

            Components.Add(splashScreen);
            Components.Add(dashboard);
                        
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            device = graphics.GraphicsDevice;

            audioEngine = new AudioEngine(@"Content\Audio\GameAudio.xgs");
            waveBank = new WaveBank(audioEngine, @"Content\Audio\Wave Bank.xwb");
            soundBank = new SoundBank(audioEngine, @"Content\Audio\Sound Bank.xsb");

            cueBackground = soundBank.GetCue("Background");
            cueRunning = soundBank.GetCue("Running");
            cueSliding = soundBank.GetCue("Sliding");
            cueBrake = soundBank.GetCue("Brake");
            cueBackground.Play();

            effect = Content.Load<Effect>("Effect//effects");
            basicEffect = new BasicEffect(GraphicsDevice);

            BoundingSphereRenderer.graphicsDevice = device;
            
            World.device = device;
            World.Content = this.Content;
            World.effect = effect;

            skybox = new World();
            skybox.LoadModel("Skybox//skybox");
            skybox.scale = 300.0f;

            ground = new World();
            ground.LoadModel("Ground//Ground");
            ground.scale = 0.1f;

            City.device = device;
            City.basicEffect = basicEffect;
            cityTexture = Content.Load<Texture2D>("texturemap");
            finalSquareTexture = Content.Load<Texture2D>("Checker");
            city = new City(cityTexture, finalSquareTexture);

            GameObject.device = device;
            GameObject.Content = this.Content;

            Car.buildingBoundingBoxes = city.buildingBoundingBoxes;
            player = new Player();
            player.position = new Vector3(405.0f, 0.0f, -400.0f);
            player.scale = 0.006f;
            player.LoadModel("PlayerCar//car");

            computer = new Computer();
            computer.position = new Vector3(405.0f, 0.0f, -390.0f);
            computer.scale = 0.13f;
            computer.LoadModel("ComputerCar//car");

            font = Content.Load<SpriteFont>("arial");
            Car.font = font;
            fontPos = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2, graphics.GraphicsDevice.Viewport.Height / 2);
            Camera.buildingBoundingBoxes = city.buildingBoundingBoxes;
            camera = new Camera(this, player.position, player.rotation);
            Components.Add(camera);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || keyboardState.IsKeyDown(Keys.Escape))
                this.Exit();

                this.IsMouseVisible = false;
                if (player.isWinner || computer.isWinner)
                {
                    gameOver = true;
                    if (player.isWinner)
                        winner = player;
                    else
                        winner = computer;
                }
                else
                {
                    HandleKeyboard();
                    player.ProcessKeyboard(gameTime);
                    player.Update(gameTime, computer);
                    computer.Move(gameTime);
                    computer.Update(gameTime, player);
                    camera.CreateLookAt(player.position, player.rotation);

                    skybox.translation = player.position;
                }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

                skybox.Draw(camera.view, camera.projection);

                ground.Draw(camera.view, camera.projection);

                city.Draw(camera.view, camera.projection);

                player.Draw(camera.view, camera.projection);

                computer.Draw(camera.view, camera.projection);

                // Uncomment next line, you can see the detectors in front of computer car
                //computer.DrawDetectors(camera.view, camera.projection);

                if (!gameOver)
                    player.DrawInfo(spriteBatch);
                else
                    DrawResult(winner);

                dashboard.DrawIcons(GraphicsDevice.PresentationParameters.BackBufferWidth);

            base.Draw(gameTime);
        }

        private void HandleKeyboard()
        {
            KeyboardState keys = Keyboard.GetState();
            if (keys.IsKeyDown(Keys.Up))
            {
                dashboard.addShield();
                if (cueRunning.IsPaused)
                {
                    cueRunning.Resume();
                }
                else if (cueRunning.IsPlaying)
                {
                    // Do nothing
                }
                else
                {
                    // If stopped, create a new cue.
                    cueRunning = soundBank.GetCue(cueRunning.Name);
                    cueRunning.Play();
                }
            }
            if (keys.IsKeyDown(Keys.Down))
            {
                if (cueBrake.IsPaused)
                {
                    cueBrake.Resume();
                }
                else if (cueBrake.IsPlaying)
                {
                    // Do nothing
                }
                else
                {
                    // If stopped, create a new cue.
                    cueBrake = soundBank.GetCue(cueBrake.Name);
                    cueBrake.Play();
                }
            }
            if (keys.IsKeyDown(Keys.Left))
            {
                if (cueSliding.IsPaused)
                {
                    cueSliding.Resume();
                }
                else if (cueSliding.IsPlaying)
                {
                    // Do nothing
                }
                else
                {
                    // If stopped, create a new cue.
                    cueSliding = soundBank.GetCue(cueSliding.Name);
                    cueSliding.Play();
                }
            }
            else if (keys.IsKeyDown(Keys.Right))
            {
                if (cueSliding.IsPaused)
                {
                    cueSliding.Resume();
                }
                else if (cueSliding.IsPlaying)
                {
                    // Do nothing
                }
                else
                {
                    // If stopped, create a new cue.
                    cueSliding = soundBank.GetCue(cueSliding.Name);
                    cueSliding.Play();
                }
            }
        }

        private void DrawResult(Car car)
        {
            if (car.GetType() == typeof(Player))
            {
                String message = "You Win!!! Score: " + car.score;
                spriteBatch.Begin();
                spriteBatch.DrawString(font, message, fontPos, Color.White);
                spriteBatch.End();
            }
            else
            {
                spriteBatch.Begin();
                spriteBatch.DrawString(font, "Game Over.", fontPos, Color.White);
                spriteBatch.End();
            }
        }
    }
}
